home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / quicktime / all macintosh / music and sound / soundsnippets / soundsnippets.c < prev    next >
Encoding:
Text File  |  2000-06-23  |  34.7 KB  |  1,169 lines

  1. //////////
  2. //
  3. //    File:        SoundSnippets.c
  4. //
  5. //    Contains:    Code snippets showing how to perform a few typical sound-related operations.
  6. //
  7. //    Written by:    Tim Monroe
  8. //                Some routines based on existing code by Jim Reekes and Kip Olson.
  9. //
  10. //    Copyright:    © 1998 by Apple Computer, Inc., all rights reserved.
  11. //
  12. //    Change History (most recent first):
  13. //
  14. //       <7>         02/03/99    rtm        minor tweaks based on AudioConvert.c sample code
  15. //       <6>         02/03/99    rtm        reworked prompt and filename handling to remove "\p" sequences
  16. //       <5>         06/25/98    rtm        added SndSnip_HasSoundManager3_1; modified the function
  17. //                                    SndSnip_PromptUserForDiskFileAndSaveCompressed to compress a
  18. //                                    file in 40K chunks
  19. //       <4>         06/22/98    rtm        added SndSnip_GetAudioSettings and SndSnip_ConvertWAVEFormats;
  20. //                                    added SndSnip_PromptUserForAudioFileAndCompress and
  21. //                                    SndSnip_PromptUserForDiskFileAndSaveCompressed, which illustrate
  22. //                                    both the standard sound compression dialog component and the
  23. //                                    sound converter routines
  24. //       <3>         05/29/98    rtm        added SndSnip_GetHardwareSettings, SndSnip_CheckVersionNumber,
  25. //                                    SndSnip_GetVolume, and SndSnip_SetVolume
  26. //       <2>         04/24/98    rtm        got bufferCmd double-buffering working
  27. //       <1>         04/17/98    rtm        first file
  28. //      
  29. //    This sample code illustrates how to perform some common sound-related operations,
  30. //    such as saving a movie's sound track into a separate file or playing a continuous sound using
  31. //    the bufferCmd sound command. This is not really a library of utilities, but more a grab-bag
  32. //    of "how-to" recipes. Here's a partial list of what's illustrated here:
  33. //
  34. //    *Save a sound-only QuickTime movie as a WAVE file.
  35. //    *Save a sound-only QuickTime movie as a file whose type the user selects.
  36. //    *Save a sound track in a QuickTime movie as a file whose type the user selects.
  37. //    *Create a handle that contains the first sound track in a QuickTime movie.
  38. //    *Open and play a WAVE file using the QuickTime API.
  39. //    *Set the volume level of a sound track in a QuickTime movie.
  40. //    *Use the bufferCmd sound command to continuously play a large buffer of audio data.
  41. //    *Get the current hardware settings.
  42. //    *Get the settings for a sound track in a QuickTime movie.
  43. //    *Get and set the current left and right volumes of a sound channel.
  44. //    *Let the user select an audio file and select its compression settings; then compress it.
  45. //
  46. //    NOTES:
  47. //
  48. //    *** (1) ***
  49. //    The function SndSnip_PromptUserForAudioFileAndCompress uses the standard sound compression dialog routines
  50. //    to elicit compression/conversion settings from the user. These routines were based on the standard image
  51. //    compression dialog routines. You can use modal-dialog filter functions and hook functions with the sound
  52. //    routines in the same way that you use them with the image routines; this, however, is not illustrated here.
  53. //    See the code snippet QTStdCompr for sample code that does illustrate these features of the standard
  54. //    compression dialog routines.
  55. //
  56. //    *** (2) ***
  57. //    The function SndSnip_PromptUserForDiskFileAndSaveCompressed uses the sound converter routines (such as
  58. //    SoundConverterConvertBuffer) to compress audio data. These routines were introduced in Sound Manager 3.2.
  59. //    If you want to run in earlier Sound Manager versions, you'll need to replace this conversion method with
  60. //    some other method. The method illustrated here is based on code in the file SoundConvert.c by Andrew Wulf,
  61. //    Apple Developer Technical Support. 
  62. //
  63. //////////
  64.  
  65. #include "SoundSnippets.h"
  66. #include "QTUtilities.h"
  67.  
  68.  
  69. // global variables
  70. SoundHeader                    gSndHeader1;
  71. SoundHeader                    gSndHeader2;
  72. Boolean                        gCurrChunkDirty = false;
  73. short                        gCurrChunkIndex;
  74. SoundHeaderPtr                gCurrChunkHeader;
  75. SndChannelPtr                gCurrChannel;
  76.  
  77.  
  78. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  79. //
  80. // Sound movie conversion routines.
  81. //
  82. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  83.  
  84. //////////
  85. //
  86. // SndSnip_SaveSoundMovieAsWAVEFile
  87. // Save a sound-only QuickTime movie as a WAVE file.
  88. //
  89. //////////
  90.  
  91. void SndSnip_SaveSoundMovieAsWAVEFile (Movie theMovie)
  92. {    
  93.     StandardFileReply        myReply;
  94.     StringPtr                 myPrompt = QTUtils_ConvertCToPascalString(kSaveSoundPrompt);
  95.     StringPtr                 myFileName = QTUtils_ConvertCToPascalString(kSaveSoundWaveName);
  96.     
  97.     // have the user select the name and location of the new WAVE file
  98.     StandardPutFile(myPrompt, myFileName, &myReply);
  99.     if (!myReply.sfGood)
  100.         return;
  101.         
  102.     // use the default progress procedure, if any
  103.     SetMovieProgressProc(theMovie, (MovieProgressUPP)-1L, 0);
  104.     
  105.     // export the movie into a file
  106.     ConvertMovieToFile(    theMovie,                    // the movie to convert
  107.                         NULL,                        // all tracks in the movie
  108.                         &myReply.sfFile,            // the output file
  109.                         kQTFileTypeWave,            // the output file type
  110.                         FOUR_CHAR_CODE('TVOD'),        // the output file creator
  111.                         smSystemScript,                // the script
  112.                         NULL,                         // no resource ID to be returned
  113.                         0L,                            // no flags
  114.                         NULL);                        // no specific component
  115.                         
  116.     free(myPrompt);
  117.     free(myFileName);
  118. }
  119.  
  120.  
  121. //////////
  122. //
  123. // SndSnip_SaveSoundMovieAsAnyTypeFile
  124. // Save a sound-only QuickTime movie as a file whose type the user selects.
  125. //
  126. //////////
  127.  
  128. void SndSnip_SaveSoundMovieAsAnyTypeFile (Movie theMovie)
  129. {    
  130.     StandardFileReply        myReply;
  131.     StringPtr                 myPrompt = QTUtils_ConvertCToPascalString(kSaveSoundPrompt);
  132.     StringPtr                 myFileName = QTUtils_ConvertCToPascalString(kSaveSoundFileName);
  133.     
  134.     // have the user select the name and location of the new file
  135.     StandardPutFile(myPrompt, myFileName, &myReply);
  136.     if (!myReply.sfGood)
  137.         return;
  138.         
  139.     // use the default progress procedure, if any
  140.     SetMovieProgressProc(theMovie, (MovieProgressUPP)-1L, 0);
  141.     
  142.     // export the movie into a file; since the flag showUserSettingsDialog is set,
  143.     // the user gets to select the audio format of the saved audio data
  144.     ConvertMovieToFile(    theMovie,                    // the movie to convert
  145.                         NULL,                        // all tracks in the movie
  146.                         &myReply.sfFile,            // the output file
  147.                         0L,                            // the output file type
  148.                         FOUR_CHAR_CODE('TVOD'),        // the output file creator
  149.                         smSystemScript,                // the script
  150.                         NULL,                         // no resource ID to be returned
  151.                         createMovieFileDeleteCurFile | movieToFileOnlyExport | showUserSettingsDialog,    
  152.                         NULL);                        // no specific component
  153.                         
  154.     free(myPrompt);
  155.     free(myFileName);
  156.  
  157. }
  158.  
  159.  
  160. //////////
  161. //
  162. // SndSnip_SaveSoundTrackAsAnyTypeFile
  163. // Save the first sound track in a QuickTime movie as a file whose type the user selects.
  164. //
  165. //////////
  166.  
  167. void SndSnip_SaveSoundTrackAsAnyTypeFile (Movie theMovie)
  168. {    
  169.     StandardFileReply        myReply;
  170.     Track                    myTrack = NULL;
  171.     StringPtr                 myPrompt = QTUtils_ConvertCToPascalString(kSaveSoundPrompt);
  172.     StringPtr                 myFileName = QTUtils_ConvertCToPascalString(kSaveSoundFileName);
  173.     
  174.     // find the first sound track in the specified movie
  175.     myTrack = GetMovieIndTrackType(theMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
  176.     if (myTrack == NULL)
  177.         return;
  178.     
  179.     // have the user select the name and location of the new file
  180.     StandardPutFile(myPrompt, myFileName, &myReply);
  181.     if (!myReply.sfGood)
  182.         return;
  183.         
  184.     // use the default progress procedure, if any
  185.     SetMovieProgressProc(theMovie, (MovieProgressUPP)-1L, 0);
  186.     
  187.     // export the specified track into a file; since the flag showUserSettingsDialog is set,
  188.     // the user gets to select the audio format of the saved audio data
  189.     ConvertMovieToFile(    theMovie,                    // the movie to convert
  190.                         myTrack,                    // save only the specified track
  191.                         &myReply.sfFile,            // the output file
  192.                         0L,                            // the output file type
  193.                         FOUR_CHAR_CODE('TVOD'),        // the output file creator
  194.                         smSystemScript,                // the script
  195.                         NULL,                         // no resource ID to be returned
  196.                         createMovieFileDeleteCurFile | movieToFileOnlyExport | showUserSettingsDialog,    
  197.                         NULL);                        // no specific component
  198.                         
  199.     free(myPrompt);
  200.     free(myFileName);
  201. }
  202.  
  203.  
  204. //////////
  205. //
  206. // SndSnip_ExtractSoundTrackIntoHandle
  207. // Create a handle that contains the first sound track in a QuickTime movie.
  208. //
  209. // NOTE: To create a handle that contains ALL the sound tracks in the movie
  210. // mixed together, pass NULL instead of myTrack to PutMovieIntoTypedHandle.
  211. //
  212. //////////
  213.  
  214. void SndSnip_ExtractSoundTrackIntoHandle (Movie theMovie)
  215. {    
  216.     Track                    myTrack = NULL;
  217.     Handle                    myHandle = NULL;
  218.     OSErr                    myErr = noErr;
  219.     
  220.     // find the first sound track in the specified movie
  221.     myTrack = GetMovieIndTrackType(theMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
  222.     if (myTrack == NULL)
  223.         goto bail;
  224.     
  225.     // create an empty handle; this will be resized by PutMovieIntoTypedHandle
  226.     myHandle = NewHandle(0);
  227.     if (myHandle == NULL)
  228.         goto bail;
  229.     
  230.     // use the default progress procedure, if any
  231.     SetMovieProgressProc(theMovie, (MovieProgressUPP)-1L, 0);
  232.     
  233.     // extract the sound track and put it into the handle
  234.     myErr = PutMovieIntoTypedHandle(
  235.                             theMovie,                    // the movie to convert
  236.                             myTrack,                    // save only the specified track
  237.                             soundListRsrc,                // make it a sound resource handle
  238.                             myHandle,                    // the handle into which to put the audio data
  239.                             0,                            // start time
  240.                             GetTrackDuration(myTrack),    // ending time
  241.                             0L,                            // flags
  242.                             NULL);                        // no specific component
  243.     if (myErr != noErr)
  244.         goto bail;
  245.         
  246.     // to prove we really did create the handle, play it
  247.     SndPlay(NULL, (SndListHandle)myHandle, false);
  248.     
  249. bail:
  250.     if (myHandle != NULL)
  251.         DisposeHandle(myHandle);
  252. }
  253.  
  254.  
  255. //////////
  256. //
  257. // SndSnip_PlayWAVEFileWithQuickTime
  258. // Open and play a WAVE file using the QuickTime API.
  259. //
  260. //////////
  261.  
  262. void SndSnip_PlayWAVEFileWithQuickTime (void)
  263. {
  264.     StandardFileReply        myReply;
  265.     SFTypeList                myTypeList = {kQTFileTypeWave, 0, 0, 0};
  266.     short                    myRefNum;
  267.     
  268.     // elicit a file from the user
  269.     StandardGetFile(NULL, 1, myTypeList, &myReply);
  270.     if (myReply.sfGood) {
  271.         Movie                myMovie;
  272.         
  273.         OpenMovieFile(&myReply.sfFile, &myRefNum, fsRdPerm);
  274.         NewMovieFromFile(&myMovie, myRefNum, NULL, (StringPtr)NULL, newMovieActive, NULL);
  275.         if (myRefNum != 0)
  276.             CloseMovieFile(myRefNum);
  277.             
  278.         // play the movie once thru
  279.         StartMovie(myMovie);
  280.         while (!IsMovieDone(myMovie))
  281.             MoviesTask(myMovie, 0);
  282.         DisposeMovie(myMovie);
  283.     }
  284. }
  285.  
  286.  
  287. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  288. //
  289. // Volume routines.
  290. //
  291. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  292.  
  293. //////////
  294. //
  295. // SndSnip_SetVolumeOfSoundTrack
  296. // Set the volume level of the first sound track in a QuickTime movie.
  297. //
  298. // The range for the theVolume parameter is 0 (no volume) to 256 (full volume).
  299. // You can also pass negative values (e.g., -128). No sound is produced for negative
  300. // values, but they are useful for maintaining the absolute value of a volume.
  301. //
  302. //////////
  303.  
  304. void SndSnip_SetVolumeOfSoundTrack (Movie theMovie, short theVolume)
  305. {
  306.     Track            myTrack = NULL;
  307.     
  308.     // find the first sound track in the specified movie
  309.     myTrack = GetMovieIndTrackType(theMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
  310.     if (myTrack == NULL)
  311.         return;
  312.     
  313.     // set the volume of the track
  314.     SetTrackVolume(myTrack, theVolume);
  315. }
  316.  
  317.  
  318. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  319. //
  320. // Double-buffering routines.
  321. //
  322. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  323.  
  324. //////////
  325. //
  326. // SndSnip_PlaySoundResourceUsingBufferCmds
  327. // Use the bufferCmd sound command to continuously play a large buffer of audio data.
  328. //
  329. // NOTE: This routine exists solely to illustrate one way to use bufferCmd sound commands
  330. // to play a buffer of audio data continuously; here, we read the audio data from a resource
  331. // and then hand chunks of it to the Sound Manager using bufferCmds. OF COURSE we could have
  332. // played the entire buffer in one fell swoop by calling SndPlay. The idea is that a real
  333. // application would read the audio data from disk in bite-sized chunks and use the techniques
  334. // illustrated here to play that data.
  335. //
  336. //////////
  337.  
  338. OSErr SndSnip_PlaySoundResourceUsingBufferCmds (void)
  339. {
  340.     Handle                    myHandle = NULL;
  341.     SoundComponentData        myResourceInfo;
  342.     unsigned long            myNumFrames;
  343.     unsigned long            myOffset;
  344.     Ptr                        myPtr = NULL;
  345.     Size                    mySize;
  346.     long                    myFreq;
  347.     SndChannelPtr            mySndChannel = NULL;
  348.     SndCallBackUPP            mySndCallBackProc;
  349.     unsigned long            myBufferSize;            // the size of the buffer we pass to each bufferCmd command
  350.     OSErr                    myErr = memFullErr;
  351.  
  352.     // get the resource data; we assume this resource contains sampled sound data
  353.     myHandle = GetResource(soundListRsrc, kSampleResourceID);
  354.     if (myHandle == NULL)
  355.         goto bail;
  356.         
  357.     // get info from the sound resource header
  358.     myErr = ParseSndHeader((SndListHandle)myHandle, &myResourceInfo, &myNumFrames, &myOffset);
  359.     if (myErr != noErr)
  360.         goto bail;
  361.     
  362.     // get the base frequency from the sampled sound header
  363.     myFreq = SndSnip_GetSndBaseFrequency(myHandle);
  364.     
  365.     // lock the handle, so we can use a pointer to cruise thru the sound data
  366.     HLockHi(myHandle);
  367.  
  368.     myPtr = *myHandle + myOffset;
  369.     mySize = GetHandleSize(myHandle) - myOffset;
  370.     myBufferSize = mySize / kNumberOfBufferChunks;
  371.     
  372.     // allocate a routine descriptor for our sound callback routine
  373.     mySndCallBackProc = NewSndCallBackProc(SndSnip_CallbackProc);
  374.  
  375.     // create storage for a new sound channel
  376.     mySndChannel = (SndChannelPtr)NewPtrClear(sizeof(SndChannel));
  377.     if (mySndChannel == NULL)
  378.         goto bail;
  379.     
  380.     // set the number of commands in the sound channel queue;
  381.     // pass the application's A5 value in the userInfo field
  382.     mySndChannel->qLength = kNumberOfCmdsInQueue;
  383. #if TARGET_OS_MAC    
  384.     mySndChannel->userInfo = SetCurrentA5();
  385. #endif
  386.     
  387.     // create the sound channel
  388.     myErr = SndNewChannel(&mySndChannel, sampledSynth, initMono, mySndCallBackProc);
  389.     if (myErr != noErr) {
  390.         DisposePtr((Ptr)mySndChannel);
  391.         goto bail;
  392.     }
  393.     
  394.     // the basic idea is this: fill two buffers with data and pass them to the Sound Manager
  395.     // using the bufferCmd sound command; each bufferCmd is immediately followed in the queue
  396.     // by a callBackCmd sound command; a callBackCmd triggers the _CheckBuffers routine, which
  397.     // installs another buffer of data and an associated callBackCmd
  398.     
  399.     // configure the first sound header
  400.     gSndHeader1.samplePtr = myPtr;
  401.     gSndHeader1.length = myBufferSize;
  402.     gSndHeader1.sampleRate = myResourceInfo.sampleRate;
  403.     gSndHeader1.loopStart = 0;
  404.     gSndHeader1.loopEnd = 0;
  405.     gSndHeader1.encode = stdSH;
  406.     gSndHeader1.baseFrequency = myFreq;
  407.     
  408.     // install the first bufferCmd, to play the first chunk of the buffer
  409.     myErr = SndSnip_InstallBufferCmd(mySndChannel, &gSndHeader1);
  410.  
  411.     // install the first callBackCmd
  412.     myErr = SndSnip_InstallCallbackCmd(mySndChannel, 0, (long)&gSndHeader1);
  413.     
  414.     // configure the second sound header
  415.     gSndHeader2.samplePtr = myPtr + myBufferSize;
  416.     gSndHeader2.length = myBufferSize;
  417.     gSndHeader2.sampleRate = myResourceInfo.sampleRate;
  418.     gSndHeader2.loopStart = 0;
  419.     gSndHeader2.loopEnd = 0;
  420.     gSndHeader2.encode = stdSH;
  421.     gSndHeader2.baseFrequency = myFreq;
  422.     
  423.     // install the second bufferCmd, to play the second chunk of the buffer
  424.     myErr = SndSnip_InstallBufferCmd(mySndChannel, &gSndHeader2);
  425.  
  426.     // install the second callBackCmd
  427.     myErr = SndSnip_InstallCallbackCmd(mySndChannel, 1, (long)&gSndHeader2);
  428.     
  429. bail:
  430.     return(myErr);
  431. }
  432.  
  433.  
  434. //////////
  435. //
  436. // SndSnip_CallbackProc
  437. // Handle callback messages. Keep it quick, okay?
  438. //
  439. //////////
  440.  
  441. PASCAL_RTN void SndSnip_CallbackProc (SndChannelPtr theChannel, SndCommand *theCommand)
  442. {
  443. #if TARGET_OS_MAC    
  444.     long        myA5;
  445.     
  446.     myA5 = SetA5(theChannel->userInfo);
  447. #endif    
  448.  
  449.     // in this sample code, param1 of theCommand is the 0-based index of the chunk of the buffer
  450.     // that just finished playing, and param2 is a pointer to the sound header for that chunk
  451.     gCurrChunkDirty = true;
  452.     gCurrChunkIndex = theCommand->param1;
  453.     gCurrChunkHeader = (SoundHeaderPtr)(theCommand->param2);
  454.     gCurrChannel = theChannel;
  455.         
  456. #if TARGET_OS_MAC    
  457.     myA5 = SetA5(myA5);
  458. #endif    
  459. }
  460.  
  461.  
  462. //////////
  463. //
  464. // SndSnip_CheckBuffers
  465. // Handle callback messages.
  466. //
  467. // This function needs to be called periodically. On MacOS, you could call this function
  468. // every time thru your event loop. On Windows, you'll need to use a different strategy;
  469. // for instance, you could install a timer that is called often enough that the bufferCmd
  470. // gets installed before the currently-playing buffer is exhausted.
  471. //
  472. //////////
  473.  
  474. void SndSnip_CheckBuffers (void)
  475. {
  476.     if (gCurrChunkDirty) {
  477.  
  478.         // install next buffer of data, if there is one
  479.         if (gCurrChunkIndex < (kNumberOfBufferChunks - 2)) {
  480.         
  481.             gCurrChunkHeader->samplePtr = gCurrChunkHeader->samplePtr + (2 * (gCurrChunkHeader->length));
  482.             
  483.             // install a bufferCmd command, to play the next buffer of data            
  484.             SndSnip_InstallBufferCmd(gCurrChannel, gCurrChunkHeader);
  485.  
  486.             // install a callBackCmd command, to be called when this buffer is finished playing        
  487.             SndSnip_InstallCallbackCmd(gCurrChannel, gCurrChunkIndex + 2, (long)gCurrChunkHeader);
  488.         }
  489.     }
  490.     
  491.     gCurrChunkDirty = false;
  492. }
  493.  
  494.  
  495. //////////
  496. //
  497. // SndSnip_InstallBufferCmd
  498. // Install a bufferCmd sound command to play the specified buffer of data.
  499. //
  500. //////////
  501.  
  502. static OSErr SndSnip_InstallBufferCmd (SndChannelPtr theChannel, SoundHeaderPtr theHeaderPtr)
  503. {
  504.     SndCommand        mySndCommand;
  505.     
  506.     mySndCommand.cmd = bufferCmd;
  507.     mySndCommand.param1 = 0;
  508.     mySndCommand.param2 = (long)theHeaderPtr;
  509.     return(SndDoCommand(theChannel, &mySndCommand, kWait));
  510. }
  511.  
  512.  
  513. //////////
  514. //
  515. // SndSnip_InstallCallbackCmd
  516. // Install a callBackCmd sound command.
  517. //
  518. //////////
  519.  
  520. static OSErr SndSnip_InstallCallbackCmd (SndChannelPtr theChannel, short theParam1, long theParam2)
  521. {
  522.     SndCommand        mySndCommand;
  523.     
  524.     mySndCommand.cmd = callBackCmd;
  525.     mySndCommand.param1 = theParam1;
  526.     mySndCommand.param2 = theParam2;
  527.     return(SndDoCommand(theChannel, &mySndCommand, kWait));
  528. }
  529.  
  530.  
  531. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  532. //
  533. // Sample sound information routines.
  534. //
  535. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  536.  
  537. //////////
  538. //
  539. // SndSnip_GetSoundHeader
  540. // Returns a pointer to the sound header in a sampled sound resource.
  541. //
  542. //////////
  543.  
  544. SoundHeaderPtr SndSnip_GetSoundHeader (Handle theSndHandle)
  545. {
  546.     SoundHeaderPtr        mySndHeader = NULL;
  547.     long                myOffset = 0;
  548.     OSErr                myErr = noErr;
  549.     
  550.     myErr = GetSoundHeaderOffset((SndListHandle)theSndHandle, &myOffset);
  551.     if (myErr == noErr)
  552.         mySndHeader = (SoundHeaderPtr)((long)*theSndHandle + myOffset);
  553.  
  554.     return(mySndHeader);
  555. }
  556.  
  557.  
  558. //////////
  559. //
  560. // SndSnip_GetSndBaseFrequency
  561. // Returns the base frequency of a sampled sound.
  562. //
  563. //////////
  564.  
  565. long SndSnip_GetSndBaseFrequency (Handle theSndHandle)
  566. {
  567.     SoundHeaderPtr        mySndHeader;
  568.     long                myBaseFreq = kMiddleC;        // a reasonable default
  569.     
  570.     mySndHeader = SndSnip_GetSoundHeader(theSndHandle);
  571.     if (mySndHeader != NULL)
  572.         myBaseFreq = mySndHeader->baseFrequency;
  573.  
  574.     return(myBaseFreq);
  575. }
  576.  
  577.  
  578. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  579. //
  580. // Settings routines.
  581. //
  582. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  583.  
  584. //////////
  585. //
  586. // SndSnip_GetHardwareSettings
  587. // Get the current hardware settings.
  588. //
  589. // Based on the function GetHardwareSettings by Kip Olson (develop, issue 24).
  590. //
  591. //////////
  592.  
  593. OSErr SndSnip_GetHardwareSettings (SndChannelPtr theChannel, SoundComponentData *theInfo)
  594. {
  595.     NumVersion            myVersion;
  596.     OSErr                myErr = noErr;
  597.     
  598.     // SndGetInfo is available only in Sound Manager 3.1 or later,    
  599.     // so check for an appropriate Sound Manager version
  600.     myVersion = SndSoundManagerVersion();
  601.  
  602.     if (!SndSnip_CheckVersionNumber(&myVersion, 3, 1, 0))
  603.         goto bail;
  604.     
  605.     myErr = SndGetInfo(theChannel, siNumberChannels, &theInfo->numChannels);
  606.     if (myErr != noErr)
  607.         goto bail;
  608.     
  609.     myErr = SndGetInfo(theChannel, siSampleRate, &theInfo->sampleRate);
  610.     if (myErr != noErr)
  611.         goto bail;
  612.     
  613.     myErr = SndGetInfo(theChannel, siSampleSize, &theInfo->sampleSize);
  614.     if (myErr != noErr)
  615.         goto bail;
  616.     
  617.     if (theInfo->sampleSize == 8)
  618.         theInfo->format = kOffsetBinary;
  619.     else
  620.         theInfo->format = kTwosComplement;
  621.  
  622. bail:        
  623.     return(myErr);
  624. }
  625.  
  626.  
  627. //////////
  628. //
  629. // SndSnip_GetAudioSettings
  630. // Get the settings for the first audio track in the specified movie.
  631. //
  632. //////////
  633.  
  634. OSErr SndSnip_GetAudioSettings (Movie theMovie, SoundComponentData *theInfo)
  635. {
  636.     Track                        myTrack = NULL;
  637.     Media                        myMedia = NULL;
  638.     SoundDescriptionHandle        myDesc = NULL;
  639.     OSErr                        myErr = paramErr;
  640.     
  641.     // get the first sound track in the specified movie
  642.     myTrack = GetMovieIndTrackType(theMovie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly);
  643.     if (myTrack == NULL)
  644.         goto bail;
  645.     
  646.     myMedia = GetTrackMedia(myTrack);
  647.     if (myMedia == NULL)
  648.         goto bail;
  649.  
  650.     myDesc = (SoundDescriptionHandle)NewHandle(0);
  651.     if (myDesc == NULL)
  652.         goto bail;
  653.  
  654.     GetMediaSampleDescription(myMedia, 1, (SampleDescriptionHandle)myDesc);
  655.     if (GetHandleSize((Handle)myDesc) == 0)
  656.         goto bail;
  657.  
  658.     theInfo->numChannels = (**myDesc).numChannels;
  659.     theInfo->sampleRate = (**myDesc).sampleRate;
  660.     theInfo->sampleSize = (**myDesc).sampleSize;
  661.     theInfo->format = (**myDesc).dataFormat;
  662.     
  663.     myErr = noErr;
  664.     
  665. bail:
  666.  
  667.     if (myDesc != NULL)
  668.         DisposeHandle((Handle)myDesc);
  669.         
  670.     return(myErr);
  671. }
  672.  
  673.  
  674. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  675. //
  676. // Sound information routines.
  677. //
  678. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  679.  
  680. //////////
  681. //
  682. // SndSnip_HasSoundManager3_1
  683. // Returns true if the installed version of the Sound Manager is at least 3.1.
  684. //
  685. //////////
  686.  
  687. Boolean SndSnip_HasSoundManager3_1 (void)
  688. {
  689.     NumVersionVariant        myVersion;
  690.     
  691.     myVersion.parts = SndSoundManagerVersion();
  692.     return(myVersion.whole >= 0x03100000);
  693. }
  694.  
  695.  
  696. //////////
  697. //
  698. // SndSnip_CheckVersionNumber
  699. // Returns true if the given version number is compatible with
  700. // (that is, not older than) version theMajor.theMinor.theBug.
  701. //
  702. //////////
  703.  
  704. Boolean SndSnip_CheckVersionNumber (
  705.     const NumVersion        *theVersion,
  706.     UInt8                    theMajor,
  707.     UInt8                    theMinor,
  708.     UInt8                    theBug)
  709. {
  710.     if (theVersion->majorRev != theMajor)
  711.         return(theVersion->majorRev > theMajor);
  712.     else
  713.         return(theVersion->minorAndBugRev >= theMinor << 4 | theBug);
  714. }
  715.  
  716.  
  717. //////////
  718. //
  719. // SndSnip_GetVolume
  720. // Get the current left and right volumes of a sound channel.
  721. //
  722. // Based on the function GetVolume by Kip Olson (develop, issue 24).
  723. //
  724. //////////
  725.  
  726. OSErr SndSnip_GetVolume (SndChannelPtr theChannel, unsigned short *theLeftVol, unsigned short *theRightVol)
  727. {
  728.     SndCommand            mySndCommand;
  729.     unsigned long        myVolume;
  730.     OSErr                myErr = noErr;
  731.  
  732.     // getVolumeCmd is available only in Sound Manager version 3.0 and later;
  733.     // you might want to check for a correct version here
  734.  
  735.     mySndCommand.cmd = getVolumeCmd;
  736.     mySndCommand.param1 = 0;
  737.     mySndCommand.param2 = (long)&myVolume;
  738.     myErr = SndDoImmediate(theChannel, &mySndCommand);
  739.     
  740.     if (myErr == noErr) {
  741.         *theLeftVol = myVolume & 0x0000ffff;
  742.         *theRightVol = myVolume >> 16;
  743.     }
  744.     
  745.     return(myErr);
  746. }
  747.  
  748.  
  749. //////////
  750. //
  751. // SndSnip_SetVolume
  752. // Set the left and right volumes of a sound channel.
  753. //
  754. // Based on the function SetVolume by Kip Olson (develop, issue 24).
  755. //
  756. //////////
  757.  
  758. OSErr SndSnip_SetVolume (SndChannelPtr theChannel, unsigned short theLeftVol, unsigned short theRightVol)
  759. {
  760.     SndCommand            mySndCommand;
  761.     OSErr                myErr = noErr;
  762.  
  763.     // volumeCmd is available only in Sound Manager version 3.0 and later;
  764.     // you might want to check for a correct version here
  765.         
  766.     mySndCommand.cmd = volumeCmd;
  767.     mySndCommand.param1 = 0;
  768.     mySndCommand.param2 = (theRightVol << 16) | theLeftVol;
  769.     
  770.     myErr = SndDoImmediate(theChannel, &mySndCommand);
  771.     return(myErr);
  772. }
  773.  
  774.  
  775. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  776. //
  777. // Movie export component routines.
  778. //
  779. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  780.  
  781. //////////
  782. //
  783. // SndSnip_ConvertWAVEFormats
  784. // Convert an 8-bit, 22 kHz WAVE file to a 16-bit, 22 kHz IMAPCM WAVE file.
  785. //
  786. //////////
  787.  
  788. OSErr SndSnip_ConvertWAVEFormats (Movie theMovie, FSSpec *theFile)
  789. {
  790.     ComponentInstance            myComponent = NULL;
  791.     SoundDescriptionHandle        myDesc = NULL;
  792.     ComponentResult                myErr = badComponentType;
  793.  
  794.     // open a movie export component
  795.     myComponent = OpenDefaultComponent(MovieExportType, kQTFileTypeWave);
  796.     if (myComponent == NULL)
  797.         goto bail;
  798.  
  799.     // create and fill in a sound description
  800.     myDesc = (SoundDescriptionHandle)NewHandleClear(sizeof(SoundDescription));
  801.     if (myDesc == NULL) {
  802.         myErr = MemError();
  803.         goto bail;
  804.     }
  805.     
  806.     (**myDesc).descSize = sizeof(SoundDescription);
  807.     (**myDesc).sampleSize = 16;
  808.     (**myDesc).sampleRate = rate22050hz;
  809.     (**myDesc).dataFormat = k16BitLittleEndianFormat;
  810.     
  811.     // tell the export component to use the specified audio characteristics
  812.     myErr = MovieExportSetSampleDescription(myComponent, (SampleDescriptionHandle)myDesc, SoundMediaType);
  813.     if (myErr != noErr)
  814.         goto bail;
  815.         
  816.     // export the movie into a file
  817.     myErr = ConvertMovieToFile(    
  818.                         theMovie,                    // the movie to convert
  819.                         NULL,                        // all tracks in the movie
  820.                         theFile,                    // the output file
  821.                         kQTFileTypeWave,            // the output file type
  822.                         FOUR_CHAR_CODE('TVOD'),        // the output file creator
  823.                         smSystemScript,                // the script
  824.                         NULL,                         // no resource ID to be returned
  825.                         0L,                            // no flags
  826.                         myComponent);                // the export component
  827.  
  828. bail:
  829.     // dispose of any storage we allocated
  830.     if (myComponent != NULL)
  831.         CloseComponent(myComponent);
  832.         
  833.     if (myDesc != NULL)
  834.         DisposeHandle((Handle)myDesc);
  835.  
  836.     return((OSErr)myErr);
  837. }
  838.  
  839.  
  840. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  841. //
  842. // Standard sound compression dialog routines.
  843. //
  844. // These routines illustrate how to use QuickTime's standard sound compression dialog routines
  845. // to get compression settings from the user and to compress a sound using those settings. See
  846. // Chapter 20 of QuickTime 3 Reference for information on the standard sound compression dialog
  847. // routines.
  848. //
  849. // These routines show how to display the standard sound compression dialog box directly; you can
  850. // also display the dialog box indirectly by using ConvertMovieToFile with the showUserSettingsDialog
  851. // flag set; see SndSnip_SaveSoundTrackAsAnyTypeFile above for an example.
  852. //
  853. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  854.  
  855. //////////
  856. //
  857. // SndSnip_PromptUserForAudioFileAndCompress
  858. // Let the user select an audio file and select its compression settings; then compress it.
  859. //
  860. //////////
  861.  
  862. void SndSnip_PromptUserForAudioFileAndCompress (void)
  863. {
  864.     SFTypeList                    myTypeList;
  865.     StandardFileReply            myReply;
  866.     short                        myRefNum = 0;
  867.     SoundComponentData            mySrcInfo;
  868.     SoundComponentData            myDstInfo;
  869.     unsigned long                myNumFrames = 0;
  870.     unsigned long                myDataOffset = 0;
  871.     ComponentInstance            myComponent = NULL;
  872.     OSErr                        myErr = noErr;
  873.  
  874.     //////////
  875.     //
  876.     // have the user select an AIFF or AIFF-C file
  877.     //
  878.     //////////
  879.  
  880.     myTypeList[0] = kQTFileTypeAIFF;
  881.     myTypeList[1] = kQTFileTypeAIFC;
  882.  
  883.     StandardGetFilePreview(NULL, 2, myTypeList, &myReply);
  884.     if (!myReply.sfGood)
  885.         goto bail;
  886.     
  887.     //////////
  888.     //
  889.     // open the selected sound file and get the characteristics of the sound
  890.     //
  891.     //////////
  892.     
  893.     myErr = FSpOpenDF(&myReply.sfFile, fsCurPerm, &myRefNum);
  894.     if (myErr != noErr)
  895.         goto bail;
  896.  
  897.     myErr = ParseAIFFHeader(myRefNum, &mySrcInfo, &myNumFrames, &myDataOffset);
  898.     if (myErr != noErr)
  899.         goto bail;
  900.     
  901.     // zero out fields we're not interested in
  902.     mySrcInfo.flags = 0L;
  903.     mySrcInfo.sampleCount = 0L;
  904.     mySrcInfo.buffer = NULL;
  905.     mySrcInfo.reserved = 0L;
  906.     
  907.     //////////
  908.     //
  909.     // display the standard sound compression dialog box
  910.     //
  911.     //////////
  912.     
  913.     // open the standard compression dialog component
  914.     myComponent = OpenDefaultComponent(StandardCompressionType, StandardCompressionSubTypeSound);
  915.     if (myComponent == NULL)
  916.         goto bail;
  917.             
  918.     // set the initial values for the dialog box, using the current characteristics of the selected sound
  919.     SCSetInfo(myComponent, scSoundSampleRateType, &(mySrcInfo.sampleRate));
  920.     SCSetInfo(myComponent, scSoundSampleSizeType, &(mySrcInfo.sampleSize));
  921.     SCSetInfo(myComponent, scSoundChannelCountType, &(mySrcInfo.numChannels));
  922.     SCSetInfo(myComponent, scSoundCompressionType, &(mySrcInfo.format));
  923.     
  924.     // request sound compression settings from the user; in other words, put up the dialog box
  925.     // (don't let "Image" in the name here fool you: it's just a relic from the fact that the
  926.     // sound compression dialog routines were based on the existing image compression dialog routines)
  927.     myErr = SCRequestImageSettings(myComponent);
  928.     if (myErr == userCanceledErr)
  929.         goto bail;
  930.  
  931.     //////////
  932.     //
  933.     // retrieve the compression/conversion settings selected by the user
  934.     //
  935.     //////////
  936.     
  937.     SCGetInfo(myComponent, scSoundSampleRateType, &(myDstInfo.sampleRate));
  938.     SCGetInfo(myComponent, scSoundSampleSizeType, &(myDstInfo.sampleSize));
  939.     SCGetInfo(myComponent, scSoundChannelCountType, &(myDstInfo.numChannels));
  940.     SCGetInfo(myComponent, scSoundCompressionType, &(myDstInfo.format));
  941.  
  942.     // any non-compressed format appears as 'raw ', which is not what the Sound Manager expects
  943.     if (myDstInfo.format == k8BitOffsetBinaryFormat)
  944.         myDstInfo.format = kSoundNotCompressed;
  945.  
  946.     // zero out fields we're not interested in
  947.     myDstInfo.flags = 0L;
  948.     myDstInfo.sampleCount = 0L;
  949.     myDstInfo.buffer = NULL;
  950.     myDstInfo.reserved = 0L;
  951.     
  952.     //////////
  953.     //
  954.     // save the compressed sound in a new file
  955.     //
  956.     //////////
  957.     
  958.     SndSnip_PromptUserForDiskFileAndSaveCompressed(myRefNum, &mySrcInfo, &myDstInfo, myDataOffset, myNumFrames);
  959.     
  960. bail:
  961.  
  962.     if (myComponent != NULL)
  963.         CloseComponent(myComponent);
  964.         
  965.     if (myRefNum != 0)
  966.         FSClose(myRefNum);
  967. }
  968.  
  969.  
  970. //////////
  971. //
  972. // SndSnip_PromptUserForDiskFileAndSaveCompressed
  973. // Let the user select a new disk file, then convert the specified audio data into that file.
  974. //
  975. // This routine uses the Sound Converter routines introduced in Sound Manager 3.2.
  976. //
  977. //////////
  978.  
  979. void SndSnip_PromptUserForDiskFileAndSaveCompressed (short theSrcRefNum, SoundComponentData *theSrcInfo, SoundComponentData *theDstInfo, unsigned long theSrcDataOffset, unsigned long theSrcNumFrames)
  980. {
  981.     StandardFileReply            myReply;
  982.     SoundConverter                myConverter = NULL;
  983.     unsigned long                myNumInputFrames, myNumInputBytes, myNumOutputBytes;
  984.     Ptr                            myInputBuffer = NULL;
  985.     Ptr                            myOutputBuffer = NULL;
  986.     short                        myDstRefNum = 0;
  987.     StringPtr                     myPrompt = QTUtils_ConvertCToPascalString(kSaveSoundPrompt);
  988.     StringPtr                     myFileName = QTUtils_ConvertCToPascalString(kSaveSoundFileName);
  989.     OSErr                        myErr = noErr;
  990.  
  991.     //////////
  992.     //
  993.     // prompt the user for a file to put the compressed sound into
  994.     //
  995.     //////////
  996.     
  997.     StandardPutFile(myPrompt, myFileName, &myReply);
  998.     if (!myReply.sfGood)
  999.         goto bail;
  1000.  
  1001.     if (myReply.sfReplacing) {
  1002.         myErr = FSpDelete(&myReply.sfFile);
  1003.         if (myErr != noErr)
  1004.             goto bail;
  1005.     }
  1006.  
  1007.     //////////
  1008.     //
  1009.     // open a sound converter
  1010.     //
  1011.     //////////
  1012.     
  1013.     // perform in non-real time for best quality
  1014.     theDstInfo->flags |= kNoRealtimeProcessing;            
  1015.  
  1016.     myErr = SoundConverterOpen(theSrcInfo, theDstInfo, &myConverter);
  1017.     if (myErr != noErr)
  1018.         goto bail;
  1019.  
  1020.     myErr = SoundConverterGetBufferSizes(myConverter, kNumberOfTargetBytes, &myNumInputFrames, &myNumInputBytes, &myNumOutputBytes);
  1021.     if (myErr != noErr)
  1022.         goto bail;
  1023.  
  1024.     //////////
  1025.     //
  1026.     // convert the sound to the desired output format
  1027.     //
  1028.     //////////
  1029.     
  1030.     // create the input and output buffers
  1031.     myInputBuffer = NewPtrClear(myNumInputBytes);
  1032.     if (myInputBuffer == NULL)
  1033.         goto bail;
  1034.  
  1035.     myOutputBuffer = NewPtrClear(myNumOutputBytes);
  1036.     if (myOutputBuffer == NULL)
  1037.         goto bail;
  1038.  
  1039.     // create and open the output file
  1040.     myErr = FSpCreate(&myReply.sfFile, FOUR_CHAR_CODE('TVOD'), kQTFileTypeAIFC, smSystemScript);
  1041.     if (myErr != noErr)
  1042.         goto bail;
  1043.  
  1044.     myErr = FSpOpenDF(&myReply.sfFile, fsRdWrPerm, &myDstRefNum);
  1045.     if (myErr != noErr)
  1046.         goto bail;
  1047.  
  1048.     SetEOF(myDstRefNum, 0L);
  1049.     
  1050.     // convert the audio data
  1051.     myErr = SoundConverterBeginConversion(myConverter);
  1052.     if (myErr == noErr) {
  1053.         long                myInputFileSize = 0L;
  1054.         unsigned long        myActOutputFrames = 0L;
  1055.         unsigned long        myActOutputSize = 0L;
  1056.         unsigned long        myAddOutputFrames = 0L;
  1057.         unsigned long        myAddOutputSize = 0L;
  1058.         long                myCurrentFrames = 0L;
  1059.         long                myNumFramesLeft = theSrcNumFrames;
  1060.         long                myBytesRead = 0L;
  1061.         long                myBytesWritten = 0L;
  1062.         long                myFramesWritten = 0L;
  1063.         
  1064.         // set up the input file for reading
  1065.         GetEOF(theSrcRefNum, &myInputFileSize);
  1066.  
  1067.         myErr = SetFPos(theSrcRefNum, fsFromStart, theSrcDataOffset);
  1068.         if (myErr != noErr)
  1069.             goto bail;
  1070.  
  1071.         myInputFileSize -= theSrcDataOffset;
  1072.  
  1073.         // set up a preliminary AIFF header for the output file;
  1074.         // we'll update the actual values later....
  1075.         myErr = SetupAIFFHeader(myDstRefNum,
  1076.                                 theDstInfo->numChannels,
  1077.                                 theDstInfo->sampleRate,
  1078.                                 theDstInfo->sampleSize,
  1079.                                 theDstInfo->format,
  1080.                                 0L,
  1081.                                 0L);
  1082.         if (myErr != noErr)
  1083.             goto bail;
  1084.         
  1085.         myCurrentFrames = myNumInputFrames;
  1086.         
  1087.         // loop through buffers of size myNumInputFrames
  1088.         while (myNumFramesLeft > 0) {
  1089.         
  1090.             // read a buffer of data from the input file        
  1091.             myBytesRead = myNumInputBytes;
  1092.             myErr = FSRead(theSrcRefNum, &myBytesRead, myInputBuffer);
  1093.             if ((myErr != noErr) && (myErr != eofErr))
  1094.                 goto bail;
  1095.  
  1096.             // convert the buffer
  1097.             myErr = SoundConverterConvertBuffer(myConverter,
  1098.                                                 myInputBuffer,
  1099.                                                 myCurrentFrames,
  1100.                                                 myOutputBuffer,
  1101.                                                 &myActOutputFrames,
  1102.                                                 &myActOutputSize);
  1103.             if (myErr != noErr)
  1104.                 goto bail;
  1105.  
  1106.             // write the converted data into the output file
  1107.             myErr = FSWrite(myDstRefNum, (long *)&myActOutputSize, myOutputBuffer);
  1108.             if (myErr != noErr)
  1109.                 goto bail;
  1110.     
  1111.             myBytesWritten += myActOutputSize;
  1112.             myFramesWritten += myActOutputFrames;
  1113.         
  1114.             // calculate remaining frames and adjust myCurrentFrames for last buffer
  1115.             myNumFramesLeft -= myCurrentFrames;
  1116.             if (myNumFramesLeft < myNumInputFrames)
  1117.                 myCurrentFrames = myNumFramesLeft;
  1118.         }
  1119.  
  1120.         // end the conversion, and see if we get back a few more bytes of data    
  1121.         myErr = SoundConverterEndConversion(myConverter, myOutputBuffer, &myAddOutputFrames, &myAddOutputSize);
  1122.         if (myErr != noErr)
  1123.             goto bail;
  1124.     
  1125.         // if we got any more data, write it to disk    
  1126.         if (myAddOutputFrames != 0L) {
  1127.             myErr = FSWrite(myDstRefNum, (long *)&myAddOutputSize, myOutputBuffer);
  1128.             if (myErr != noErr)
  1129.                 goto bail;
  1130.         }
  1131.         
  1132.         myErr = SetFPos(myDstRefNum, fsFromStart, 0);
  1133.         if (myErr != noErr)
  1134.             goto bail;
  1135.     
  1136.         // update AIFF header
  1137.         myErr = SetupAIFFHeader(
  1138.                             myDstRefNum,
  1139.                             theDstInfo->numChannels,
  1140.                             theDstInfo->sampleRate,
  1141.                             theDstInfo->sampleSize,
  1142.                             theDstInfo->format,
  1143.                             myBytesWritten + myAddOutputSize,
  1144.                             myFramesWritten + myAddOutputFrames);
  1145.     }
  1146.  
  1147. bail:
  1148.  
  1149.     free(myPrompt);
  1150.     free(myFileName);
  1151.  
  1152.     if (myConverter != NULL)
  1153.         SoundConverterClose(myConverter);    
  1154.  
  1155.     if (myInputBuffer != NULL)
  1156.         DisposePtr(myInputBuffer);
  1157.  
  1158.     if (myOutputBuffer != NULL)
  1159.         DisposePtr(myOutputBuffer);
  1160.         
  1161.     if (myDstRefNum != 0)
  1162.         FSClose(myDstRefNum);
  1163.  
  1164. #if TARGET_OS_MAC
  1165.     // make sure the file data gets written out to disk
  1166.     FlushVol("\p", myReply.sfFile.vRefNum);
  1167. #endif
  1168. }
  1169.